﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Citrix.XaAdminMobile.ViewModel;
using Citrix.Cmp;
using Citrix.XaAdminMobile.Util;

namespace Citrix.XaAdminMobile
{
    /// <summary>
    /// The phone layout navigation implementation.
    /// </summary>
    internal class PhoneNavigation : INavigation
    {
        /// <summary>
        /// The currently visible page (view).
        /// </summary>
        private IPage currentPage;

        /// <summary>
        /// Holds the user's navigation history. This queue is used to implement back
        /// navigation.
        /// </summary>
        private List<IPage> navigationHistory;

        /// <summary>
        /// The page changed event handlers.
        /// </summary>
        private List<WeakReference> eventHandlers;

        /// <summary>
        /// A reference back to the root v-m object.
        /// </summary>
        private readonly ViewModelManager vmRoot;

        /// <summary>
        /// Ctor.
        /// </summary>
        /// <param name="vmManager">The root v-m object.</param>
        /// <param name="startingPage">The starting page.</param>
        /// <exception cref="ArgumentNullException">Thrown if any arg is null.</exception>
        internal PhoneNavigation(ViewModelManager vmManager, IPage startingPage)
        {
            if (null == vmManager)
            {
                throw new ArgumentNullException("vmManager");
            }
            if (null == startingPage)
            {
                throw new ArgumentNullException("startingPage");
            }

            this.vmRoot = vmManager;
            this.currentPage = startingPage;
            this.navigationHistory = BuildHistory(startingPage);
        }

        /// <summary>
        /// The current page to display to the user.
        /// </summary>
        public IPage CurrentPage
        {
            get { return this.currentPage; }
        }

        /// <summary>
        /// Returns the current back button navigation target that should be applied based on
        /// the navigation state.
        /// </summary>
        public Cmp.ButtonTarget BackButtonTarget
        {
            get
            {
                if (this.navigationHistory.Count > 1)
                {
                    return ButtonTarget.Server;
                }
                else
                {
                    return ButtonTarget.ClientDevice;
                }
            }
        }

        /// <summary>
        /// Converts the current navigation state of this layout to a different layout.
        /// </summary>
        /// <param name="layout">The target navigation layout.</param>
        /// <returns>The new navigation implementation.</returns>
        public INavigation Convert(NavigationLayout layout)
        {
            if (layout == NavigationLayout.Phone)
            {
                return this;
            }
            else
            {
                return new TabletNavigation(this.vmRoot, this.currentPage);
            }
        }

        /// <summary>
        /// Checks whether or not the specified page is being displayed.
        /// </summary>
        /// <param name="page">The page to check.</param>
        /// <returns>True if visible, false if not.</returns>
        public bool IsActive(IPage page)
        {
            return object.ReferenceEquals(this.currentPage, page);
        }

        /// <summary>
        /// Performs a back navigation operation to the previous page in the navigation history.
        /// </summary>
        public void NavigateBack()
        {
            if (this.navigationHistory.Count > 1)
            {
                var oldPage = this.currentPage;
                this.navigationHistory.Pop();
                this.currentPage = this.navigationHistory.Last();
         
                oldPage.NotifyActiveChanged(false);
                this.currentPage.NotifyActiveChanged(true);
                WeakRefEventHelper.CallWeakReferenceHandlers(this.eventHandlers, this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Requests that the specified page be displayed.
        /// </summary>
        /// <param name="page">The page to display.</param>
        public void NavigateForward(IPage page)
        {
            if (null != page && !object.ReferenceEquals(page, this.currentPage))
            {
                var oldPage = this.currentPage;
                this.currentPage = page;

                // Purge the nav history when we navigate to a root page.
                if (IsRootPage(page))
                {
                    this.navigationHistory.Clear();
                }

                this.navigationHistory.Add(page);
                oldPage.NotifyActiveChanged(false);
                this.currentPage.NotifyActiveChanged(true);

                WeakRefEventHelper.CallWeakReferenceHandlers(this.eventHandlers, this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handler for a physical button press on the device.
        /// </summary>
        /// <param name="buttonId">The button that was pressed.</param>
        public void NotifyButtonPressed(Cmp.ButtonId buttonId)
        {
            if (buttonId == ButtonId.Back)
            {
                if (this.currentPage != null)
                {
                    this.currentPage.NotifyBackButtonPressed();
                }
            }
        }

        /// <summary>
        /// Fired whenever the page(s) to be displayed to the user have changed.
        /// </summary>
        public event EventHandler PageChanged
        {
            add
            {
                WeakRefEventHelper.AddWeakReferenceHandler(ref this.eventHandlers, value);
            }
            remove
            {
                WeakRefEventHelper.RemoveWeakReferenceHandler(this.eventHandlers, value);
            }
        }

        /// <summary>
        /// Given any page in the navigation hierarchy, this function will recreate the
        /// navigation history. It is important to build the history correctly to properly
        /// support roaming between different layouts.
        /// </summary>
        /// <param name="startingPage">The current page.</param>
        /// <returns>The navigation hierarchy.</returns>
        private List<IPage> BuildHistory(IPage startingPage)
        {
            List<IPage> history = new List<IPage>();
            if (!IsRootPage(startingPage))
            {
                if (startingPage is AddServerViewModel)
                {
                    history.Add(this.vmRoot.SettingsViewModel);
                }
                else if (startingPage is SessionViewModel)
                {
                    history.Add(this.vmRoot.SessionsViewModel);
                }
                else if (startingPage is ServerViewModel)
                {
                    history.Add(this.vmRoot.ServersViewModel);
                }
                else
                {
                    System.Diagnostics.Debug.Assert(false, "Unknown non-root page.");

                    // Just use the sessions page as a default so back nav will work.
                    history.Add(this.vmRoot.SessionsViewModel);
                }
            }
            history.Add(startingPage);

            return history;
        }

        /// <summary>
        /// Whether the supplied page is one of the root pages in the navigation hierarchy.
        /// </summary>
        /// <param name="page">The page to check, must not be null.</param>
        /// <returns>True if a root page, false if not.</returns>
        private bool IsRootPage(IPage page)
        {
            Type pageType = page.GetType();

            return
                pageType == typeof(SessionsViewModel) ||
                pageType == typeof(ServersViewModel) ||
                pageType == typeof(SettingsViewModel);
        }
    }
}
